//
// Created by wangwanlong on 2024/10/29.
//

#ifndef MKAHYPAR_MACROS_H
#define MKAHYPAR_MACROS_H
#include "mkahypar/utils/logger.h"

#if defined(__GNUC__) || defined(__clang__)
// branch prediction
#define likely(x)      __builtin_expect(!!(x), 1)
#define unlikely(x)    __builtin_expect(!!(x), 0)
#else
#define likely(x)   x
#define unlikely(x) x
#endif

#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)

// http://stackoverflow.com/questions/195975/how-to-make-a-char-string-from-a-c-macros-value#196093
#define QUOTE(name) #name
#define STR(macro) QUOTE(macro)

#define  JOIN(a, b)       a ## b


//打印日志
#define LOGCC(cond, newline) \
  !(cond) ? (void)0 :        \
  mkahypar::LoggerVoidify() & mkahypar::Logger(newline)

#define DBGCC(cond, newline) \
  !(cond) ? (void)0 :        \
  mkahypar::LoggerVoidify() & mkahypar::Logger(newline, __FILENAME__, __FUNCTION__, __LINE__)

//打印信息到标准cout并换行
#define LOG LOGCC(true, true)
#define DBG  DBGCC(false, true)

//不换行
#define LLOG LOGCC(true, false)

//打印报错信息
#define ERROREXIT(...) do { \
    std::string msg = #__VA_ARGS__; \
    std::cerr << "Error at " << __FILE__ << ":" << __LINE__ << " in " << __func__ << "(): " << msg << std::endl; \
    exit(EXIT_FAILURE); \
} while(0)

#define V(X) #X << "=" << X

#define EXPAND(X) X
#define NARG(...) EXPAND(NARG_(__VA_ARGS__, RSEQ_N()))
#define NARG_(...) EXPAND(ARG_N(__VA_ARGS__))
#define ARG_N(_1, _2, N, ...) N
#define RSEQ_N() 2, 1, 0

#if defined(_MSC_VER)
#define __PRETTY_FUNCTION__ __FUNCTION__
#endif

#ifdef MKAHYPAR_USE_ASSERTIONS
#ifdef MKAHYPAR_USE_STANDARD_ASSERTIONS
#define ASSERT_2(cond, msg) \
  assert(cond)
#else
#define ASSERT_2(cond, msg)                   \
  do {                                        \
    if (!(cond)) {                            \
      DBG1 << "Assertion `" #cond "` failed:" \
           << msg;                            \
      std::abort();                           \
    }                                         \
  } while (0)
#endif

  #define ASSERT_1(cond) ASSERT_2(cond, "")
#else
#define ASSERT_2(cond, msg)
#define ASSERT_1(cond)
#endif

#define ASSERT_(N) ASSERT_ ## N
#define ASSERT_EVAL(N) ASSERT_(N)
#define ASSERT(...) EXPAND(ASSERT_EVAL(EXPAND(NARG(__VA_ARGS__)))(__VA_ARGS__))

// *** an always-on ASSERT
#ifdef MKAHYPAR_USE_STANDARD_ASSERTIONS
#define ALWAYS_ASSERT_2(cond, msg) \
  assert(cond)
# else
#define ALWAYS_ASSERT_2(cond, msg)            \
  do {                                        \
    if (!(cond)) {                            \
      DBG1 << "Assertion `" #cond "` failed:" \
           << msg;                            \
      std::abort();                           \
    }                                         \
  } while (0)
#endif
#define ALWAYS_ASSERT_(N) ALWAYS_ASSERT_ ## N
#define ALWAYS_ASSERT_EVAL(N) ALWAYS_ASSERT_(N)
#define ALWAYS_ASSERT(...) EXPAND(ALWAYS_ASSERT_EVAL(EXPAND(NARG(__VA_ARGS__)))(__VA_ARGS__))

// definitions for heavy assertions
#define HEAVY_ASSERT0(cond) \
  !(enable_heavy_assert) ? (void)0 : [&]() { ASSERT(cond); } ()
#define HEAVY_ASSERT1(cond, msg) \
  !(enable_heavy_assert) ? (void)0 : [&]() { ASSERT(cond, msg); } ()

#ifdef MKAHYPAR_ENABLE_HEAVY_PREPROCESSING_ASSERTIONS
#define HEAVY_PREPROCESSING_ASSERT_1(cond) ASSERT(cond)
  #define HEAVY_PREPROCESSING_ASSERT_2(cond, msg) ASSERT(cond, msg)
#else
#define HEAVY_PREPROCESSING_ASSERT_1(cond) HEAVY_ASSERT0(cond)
#define HEAVY_PREPROCESSING_ASSERT_2(cond, msg) HEAVY_ASSERT1(cond, msg)
#endif

#ifdef MKAHYPAR_ENABLE_HEAVY_DATA_STRUCTURE_ASSERTIONS
#define HEAVY_DATA_STRUCTURE_ASSERT_1(cond) ASSERT(cond)
  #define HEAVY_DATA_STRUCTURE_ASSERT_2(cond, msg) ASSERT(cond, msg)
#else
#define HEAVY_DATA_STRUCTURE_ASSERT_1(cond) HEAVY_ASSERT0(cond)
#define HEAVY_DATA_STRUCTURE_ASSERT_2(cond, msg) HEAVY_ASSERT1(cond, msg)
#endif



#define HEAVY_ASSERT_(TYPE, N) HEAVY_ ## TYPE ## _ASSERT_ ## N
#define HEAVY_ASSERT_EVAL(TYPE, N) HEAVY_ASSERT_(TYPE, N)

#define HEAVY_DATA_STRUCTURE_ASSERT(...) EXPAND(HEAVY_ASSERT_EVAL(DATA_STRUCTURE, EXPAND(NARG(__VA_ARGS__)))(__VA_ARGS__))
#define HEAVY_PREPROCESSING_ASSERT(...) EXPAND(HEAVY_ASSERT_EVAL(PREPROCESSING, EXPAND(NARG(__VA_ARGS__)))(__VA_ARGS__))
#define HEAVY_COARSENING_ASSERT(...) EXPAND(HEAVY_ASSERT_EVAL(COARSENING, EXPAND(NARG(__VA_ARGS__)))(__VA_ARGS__))
#define HEAVY_INITIAL_PARTITIONING_ASSERT(...) EXPAND(HEAVY_ASSERT_EVAL(INITIAL_PARTITIONING, EXPAND(NARG(__VA_ARGS__)))(__VA_ARGS__))
#define HEAVY_REFINEMENT_ASSERT(...) EXPAND(HEAVY_ASSERT_EVAL(REFINEMENT, EXPAND(NARG(__VA_ARGS__)))(__VA_ARGS__))

#define ONLYDEBUG(x) ((void)x)
#define UNUSED_FUNCTION(x) ((void)x)

#if defined(__GNUC__) || defined(__clang__)
#define UTILS_UNUSED __attribute__ ((unused))
#define UNUSED(name) unused_ ## name UTILS_UNUSED
#else
#define UNUSED(name) name
#endif

template <typename T>
void unused(T&&) {
  // Used to avoid warnings of unused variables
}


#if defined(__GNUC__) || defined(__clang__)
#define MKAHYPAR_ATTRIBUTE_ALWAYS_INLINE __attribute__ ((always_inline))
#else
#define MKAHYPAR_ATTRIBUTE_ALWAYS_INLINE
#endif

#endif //MKAHYPAR_MACROS_H

